﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameEngine.DrawEvaluator;
using GameEngine.MoveGenerators;
using GameEngine.MoveHistory;
using GameEngine.PositionConverter;

namespace GameEngine
{
    /// <summary>
    /// Class responsible for building the whole infrastructure of GameManager for managing checkers
    /// games of various types
    /// </summary>
    public static class GameManagerFactory
    {
        /// <summary>
        /// Keeps dictionary of checkers settings
        /// </summary>
        private static Dictionary<enumGameType, CheckersSettings> _checkersSettings;

        /// <summary>
        /// Sets up various Checkers settings
        /// </summary>
        static GameManagerFactory()
        {
            _checkersSettings = new Dictionary<enumGameType, CheckersSettings>();

            // Czech Checkers settings
            CheckersSettings czechCheckers = new CheckersSettings();
            czechCheckers.BoardSize = 8;
            czechCheckers.CaptureContraintList.Add(enumCaptureConstraint.KingPriority);
            czechCheckers.FirstToMove = enumFigureColor.White;
            czechCheckers.NumFigureRows = 3;
            czechCheckers.WhiteMovesUp = true;
            czechCheckers.IsBoardFlipped = false;
            czechCheckers.PlayedOnWhiteFields = false;
            czechCheckers.ContinueJumpAfterPromotion = false;
            _checkersSettings[enumGameType.Czech] = czechCheckers;

            // English Checkers settings
            CheckersSettings englishCheckers = new CheckersSettings();
            englishCheckers.BoardSize = 8;
            englishCheckers.CaptureContraintList.Add(enumCaptureConstraint.None);
            englishCheckers.FirstToMove = enumFigureColor.Black;
            englishCheckers.NumFigureRows = 3;
            englishCheckers.WhiteMovesUp = true;
            englishCheckers.IsBoardFlipped = false;
            englishCheckers.PlayedOnWhiteFields = false;
            englishCheckers.ContinueJumpAfterPromotion = false;
            _checkersSettings[enumGameType.English] = englishCheckers;

            // International Checkers settings
            CheckersSettings internationalCheckers = new CheckersSettings();
            internationalCheckers.BoardSize = 10;
            internationalCheckers.CaptureContraintList.Add(enumCaptureConstraint.LongestJumpPriority);
            internationalCheckers.FirstToMove = enumFigureColor.White;
            internationalCheckers.NumFigureRows = 4;
            internationalCheckers.WhiteMovesUp = true;
            internationalCheckers.IsBoardFlipped = false;
            internationalCheckers.PlayedOnWhiteFields = false;
            internationalCheckers.ContinueJumpAfterPromotion = false;
            _checkersSettings[enumGameType.International] = internationalCheckers;

            // Italian Checkers settings
            CheckersSettings italianCheckers = new CheckersSettings();
            italianCheckers.BoardSize = 8;
            italianCheckers.CaptureContraintList.Add(enumCaptureConstraint.LongestJumpPriority);
            italianCheckers.CaptureContraintList.Add(enumCaptureConstraint.KingPriority);
            italianCheckers.CaptureContraintList.Add(enumCaptureConstraint.CaptureMaximumKings);
            italianCheckers.CaptureContraintList.Add(enumCaptureConstraint.CaptureNearestKing);
            italianCheckers.FirstToMove = enumFigureColor.White;
            italianCheckers.NumFigureRows = 3;
            italianCheckers.WhiteMovesUp = true;
            italianCheckers.IsBoardFlipped = true;
            italianCheckers.PlayedOnWhiteFields = false;
            italianCheckers.ContinueJumpAfterPromotion = false;
            _checkersSettings[enumGameType.Italian] = italianCheckers;

            // Russian Checkers settings
            CheckersSettings russianCheckers = new CheckersSettings();
            russianCheckers.BoardSize = 8;
            russianCheckers.CaptureContraintList.Add(enumCaptureConstraint.None);
            russianCheckers.FirstToMove = enumFigureColor.White;
            russianCheckers.NumFigureRows = 3;
            russianCheckers.WhiteMovesUp = true;
            russianCheckers.IsBoardFlipped = false;
            russianCheckers.PlayedOnWhiteFields = false;
            russianCheckers.ContinueJumpAfterPromotion = true;
            _checkersSettings[enumGameType.Russian] = russianCheckers;

            // South-German Draughts settings
            CheckersSettings germanCheckers = new CheckersSettings();
            germanCheckers.BoardSize = 8;
            germanCheckers.CaptureContraintList.Add(enumCaptureConstraint.None);
            germanCheckers.FirstToMove = enumFigureColor.White;
            germanCheckers.NumFigureRows = 3;
            germanCheckers.WhiteMovesUp = true;
            germanCheckers.IsBoardFlipped = false;
            germanCheckers.PlayedOnWhiteFields = false;
            germanCheckers.ContinueJumpAfterPromotion = false;
            _checkersSettings[enumGameType.SouthGerman] = germanCheckers;

            // South-German Draughts settings
            CheckersSettings spanishCheckers = new CheckersSettings();
            spanishCheckers.BoardSize = 8;
            spanishCheckers.CaptureContraintList.Add(enumCaptureConstraint.LongestJumpPriority);
            spanishCheckers.CaptureContraintList.Add(enumCaptureConstraint.CaptureMaximumKings);
            spanishCheckers.FirstToMove = enumFigureColor.White;
            spanishCheckers.NumFigureRows = 3;
            spanishCheckers.WhiteMovesUp = true;
            spanishCheckers.IsBoardFlipped = false;
            spanishCheckers.PlayedOnWhiteFields = true;
            spanishCheckers.ContinueJumpAfterPromotion = false;
            _checkersSettings[enumGameType.Spanish] = spanishCheckers;           
        }

        /// <summary>
        /// Creates the game board
        /// </summary>
        private static GameBoard CreateGameBoard(int size, bool flipped, bool playedOnWhiteFields)
        {
            return new GameBoard(size, flipped, playedOnWhiteFields);
        }

        /// <summary>
        /// Creates the draw evaluator for specific checkers type
        /// </summary>
        private static IDrawEvaluator CreateDrawEvaluator(enumGameType eGameType, GameBoard gameBoard)
        {
            switch (eGameType)
            {
                case enumGameType.Czech:
                    CzechDrawEvaluator czechDraw = new CzechDrawEvaluator();
                    czechDraw.GameBoard = gameBoard;
                    return czechDraw;
                case enumGameType.English:
                    DrawEvaluator.DrawEvaluator englishEval = new DrawEvaluator.DrawEvaluator();
                    englishEval.GameBoard = gameBoard;
                    return englishEval;
                case enumGameType.International:
                    InternationalDrawEvaluator intEval = new InternationalDrawEvaluator();
                    intEval.GameBoard = gameBoard;
                    return intEval;
                case enumGameType.Italian:
                    ItalianDrawEvaluator itEval = new ItalianDrawEvaluator();
                    itEval.GameBoard = gameBoard;
                    return itEval;
                case enumGameType.Russian:
                    RussianDrawEvaluator rusEval = new RussianDrawEvaluator();
                    rusEval.GameBoard = gameBoard;
                    return rusEval;
                case enumGameType.SouthGerman:
                    SouthGermanDrawEvaluator gerEval = new SouthGermanDrawEvaluator();
                    gerEval.GameBoard = gameBoard;
                    return gerEval;
                case enumGameType.Spanish:
                    SpanishDrawEvaluator spanEval = new SpanishDrawEvaluator();
                    spanEval.GameBoard = gameBoard;
                    return spanEval;
                    
                default:
                    return null;
                    throw new Exception("Unknown Checkers Type");
            } // switch
        }

        /// <summary>
        /// Creates move generator for man for specific checkers
        /// </summary>
        private static IManMoveGenerator CreateManMoveGenerator(enumGameType eGameType, GameBoard gameBoard)
        {
            ClassicManMoveGenerator moveGen = null;
            switch (eGameType)
            {
                case enumGameType.Czech:
                case enumGameType.English:
                case enumGameType.SouthGerman:
                case enumGameType.Spanish:
                    moveGen = new ClassicManMoveGenerator();
                    break;
                case enumGameType.Italian:
                    moveGen = new ClassicManMoveGenerator(false);
                    break;
                case enumGameType.International:
                case enumGameType.Russian:
                    moveGen = new JumpBackManMoveGenerator();
                    break;
                default:
                    break;
                    throw new Exception("Unknown Checkers Type");
            }

            moveGen.GameBoard = gameBoard;
            return moveGen;
        }

        /// <summary>
        /// Creates the king move generator for specific checkers
        /// </summary>
        private static IKingMoveGenerator CreateKingMoveGenerator(enumGameType eGameType, GameBoard gameBoard)
        {
            BaseKingMoveGenerator kingMoveGen = null;
            switch (eGameType)
            {
                case enumGameType.English:
                case enumGameType.Italian:
                    kingMoveGen = new ShortKingMoveGenerator();
                    break;
                case enumGameType.Czech:
                case enumGameType.International:
                case enumGameType.Russian:
                case enumGameType.Spanish:
                    kingMoveGen = new LongKingMoveGenerator();
                    break;
                case enumGameType.SouthGerman:
                    kingMoveGen = new LongKingMoveGenerator(true);
                    break;
                default:
                    break;
                    throw new Exception("Unknown Checkers Type");
            } // switch

            kingMoveGen.GameBoard = gameBoard;
            return kingMoveGen;
        }

        /// <summary>
        /// Creates the position converter
        /// </summary>
        private static IPositionConverter CreatePositionConverter(enumGameType eGameType)
        {
            return PositionConverterFactory.Create(eGameType);
        }

        /// <summary>
        /// Creates class for checking capture rules, sets specific rules from Checkers settings
        /// </summary>
        private static CaptureConstraintEvaluator CreateCaptureContraintEvaluator(CheckersSettings settings,
            GameBoard gameBoard, IManMoveGenerator manGen, IKingMoveGenerator kingGen)
        {
            CaptureConstraintEvaluator eval = new CaptureConstraintEvaluator();
            eval.GameBoard = gameBoard;
            eval.CaptureConstraintList.AddRange(settings.CaptureContraintList);
            eval.KingMoveGenerator = kingGen;
            eval.ManMoveGenerator = manGen;
            eval.WhiteMovesUp = settings.WhiteMovesUp;
            eval.ContinueJumpAfterPromotion = settings.ContinueJumpAfterPromotion;

            return eval;
        }

        /// <summary>
        /// Class responsible for creating move history class
        /// </summary>
        private static MoveHistoryFacade CreateMoveHistory(GameBoard gameBoard, enumHistoryModel eModelType)
        {
            MoveHistoryFacade moveHistory = new MoveHistoryFacade(eModelType);
            moveHistory.GameBoard = gameBoard;
            return moveHistory;
        }

        
        /// <summary>
        /// Creates the main class responsible for keeping rules of specific checkers
        /// </summary>
        private static GameRuleController CreateGameRuleController(CheckersSettings settings,
            GameBoard gameBoard, IManMoveGenerator manGen, IKingMoveGenerator kingGen, IDrawEvaluator drawEval,
            CaptureConstraintEvaluator captureEval, IPositionConverter posConverter)
        {
            GameRuleController ruleCon = new GameRuleController();
            ruleCon.CaptureConstraintEvaluator = captureEval;
            ruleCon.DrawEvaluator = drawEval;
            ruleCon.GameBoard = gameBoard;
            ruleCon.CheckersSettings = settings;
            ruleCon.KingMoveGenerator = kingGen;
            ruleCon.ManMoveGenerator = manGen;
            ruleCon.PositionConverter = posConverter;

            return ruleCon;
        }



        public static GameManager CreateGameManager(enumGameType type, enumHistoryModel modelType = enumHistoryModel.Linear)
        {
            // get checkers settings 
            CheckersSettings settings = _checkersSettings[type];

            // do the building specific game-rule partial classes
            GameBoard gameBoard = CreateGameBoard(settings.BoardSize, settings.IsBoardFlipped, 
                                                settings.PlayedOnWhiteFields);
            IDrawEvaluator drawEval = CreateDrawEvaluator(type, gameBoard);
            IManMoveGenerator manGen = CreateManMoveGenerator(type, gameBoard);
            IKingMoveGenerator kingGen = CreateKingMoveGenerator(type, gameBoard);
            IPositionConverter posConv = CreatePositionConverter(type);
            CaptureConstraintEvaluator captureEval = CreateCaptureContraintEvaluator(settings,
                    gameBoard, manGen, kingGen);
            MoveHistoryFacade moveHistory = CreateMoveHistory(gameBoard, modelType);
            GameRuleController ruleCon = CreateGameRuleController(settings, gameBoard, manGen, kingGen,
                                drawEval, captureEval, posConv);

            // build final object
            GameManager gameMng = new GameManager();
            gameMng.GameType = type;
            gameMng.PDNHeaders = new Dictionary<string, string>();
            gameMng.GameBoard = gameBoard;
            gameMng.MoveHistory = moveHistory;
            gameMng.GameRuleController = ruleCon;
            gameMng.PieceRows = settings.NumFigureRows;

            // return it
            return gameMng;         
        }
    }
}
